package com.cold.demo.template.filter;

import com.cold.demo.template.mapper.CommonMapper;
import javafx.application.Application;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 只记录controller下面的请求路径，其他的静态资源不进行拦截
 */
@Order(1)
@WebFilter
@Component
public class RequestLogFilter extends BaseFilter implements ApplicationContextAware, InitializingBean {

    private final static ThreadLocal<RequestLog> threadLocalLog = new ThreadLocal<>();

    @Autowired
    private CommonMapper commonMapper;

    private ApplicationContext applicationContext;

    private ExecutorService logExecutor = Executors.newSingleThreadExecutor();

    private RequestMappingHandlerMapping mapping;

    private Map<RequestMappingInfo, HandlerMethod> handlerMethods;

    private Set<RequestMappingInfo> requestMappingInfos;


    public RequestLogFilter(RequestMappingHandlerMapping mapping) {
        this.mapping = mapping;
    }

    public static RequestLog getRequestLog() {
        RequestLog requestLog = threadLocalLog.get();
        if (requestLog != null) {
            return requestLog;
        }
        requestLog = new RequestLog();
        threadLocalLog.set(requestLog);
        return requestLog;
    }


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        if (!needIntercept((HttpServletRequest)servletRequest)) {
            chain.doFilter(servletRequest, servletResponse);
            return;
        }
        RequestLog requestLog = RequestLogFilter.getRequestLog();

        HttpServletRequest  request  = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        executeBefore(request, requestLog);
        try {
            chain.doFilter(request, response);
        } finally {
            executeAfter(response, requestLog);
        }
    }

    /*
        只拦截已存在的url
     */
    private boolean needIntercept(HttpServletRequest servletRequest) {
        String contextPath = servletRequest.getContextPath();

        for (RequestMappingInfo requestMappingInfo : requestMappingInfos) {
            RequestMappingInfo matchingCondition = requestMappingInfo.getMatchingCondition(servletRequest);
            if (matchingCondition != null) {
                return true;
            }
        }
        return false;
    }

    /*
     * 获取请求时的数据
     */
    private void executeBefore(HttpServletRequest request, RequestLog requestLog) {
        Date startTime = new Date();
        requestLog.setRequest_start_time(startTime);
        requestLog.setRequest_url(request.getRequestURL().toString());// 全路径
        requestLog.setContent_type(request.getContentType());
        requestLog.setCookies(request.getCookies() == null ? null : Arrays.stream(request.getCookies())
                .map(cookie -> cookie.getName() + "=" + cookie.getValue() + "; ")
                .collect(Collectors.joining("")));

        StringBuilder       headers     = new StringBuilder();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName  = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            headers.append(headerName).append(": ").append(headerValue).append('\n');
        }
        requestLog.setRequest_headers(headers.toString());


        requestLog.setMethod(request.getMethod());
        requestLog.setRemote_addr(request.getRemoteAddr());
        requestLog.setRemote_host(request.getRemoteHost());
        requestLog.setRemote_port(request.getRemotePort());

        Map<String, String[]> parameterMap = request.getParameterMap();
        if (!CollectionUtils.isEmpty(parameterMap)) {
            StringBuilder params = new StringBuilder();
            parameterMap.forEach((k, vs) -> {
                if (vs == null) {
                    params.append(k).append("=&");
                } else {
                    for (String v : vs) {
                        params.append(k).append("=").append(v == null ? "" : v).append("&");
                    }
                }
            });
            String paramStr = params.toString();
            if (paramStr.length() > 0) {
                paramStr = paramStr.substring(0, paramStr.length() - 1);
                requestLog.setRequest_params(paramStr);
            }
        }
    }

    /*
     * 获取请求后的数据并插入数据库
     */
    private void executeAfter(HttpServletResponse response, RequestLog requestLog) {
        logExecutor.execute(() -> {
            StringBuilder      headers     = new StringBuilder();
            Collection<String> headerNames = response.getHeaderNames();
            for (String headerName : headerNames) {
                String headerValue = response.getHeader(headerName);
                headers.append(headerName).append(": ").append(headerValue).append('\n');
            }
            requestLog.setResponse_headers(headers.toString());
            requestLog.setResponse_status(response.getStatus());

            Date endTime = new Date();
            requestLog.setRequest_end_time(endTime);
            requestLog.setRequest_consume_time(endTime.getTime() - requestLog.getRequest_start_time().getTime());
            StringBuilder logMsg = new StringBuilder();
            logMsg.append("\n\t\t请求路径：").append(requestLog.getRequest_url());
            logMsg.append("\n\t\t请求参数：").append(requestLog.getRequest_params());
            logMsg.append("\n\t\t请求体：").append(requestLog.getRequest_body());
            if (requestLog.getResponse_status() != 200) {
                logMsg.append("\n\t\t响应状态码：").append(requestLog.getResponse_status());
            }
            logMsg.append("\n\t\t响应体：").append(requestLog.getResponse_body());
            logMsg.append("\n\t\t响应时间：").append(requestLog.getRequest_consume_time()).append("ms");
            if (HttpServletResponse.SC_FOUND == requestLog.getResponse_status()) {
                requestLog.setRedirect_url(response.getHeader("Location"));
                logMsg.append("\n\t\t重定向地址：").append(requestLog.getRedirect_url());
            }
            log.info(logMsg.toString());
//            commonMapper.insertLog(requestLog);
        });
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.handlerMethods = this.mapping.getHandlerMethods();
        requestMappingInfos = handlerMethods.keySet();
    }
}
